﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.ServiceModel;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using SharedGenomics.Core;
using SharedGenomics.Domain;
using SharedGenomics.Annotations;
using SharedGenomics.Workbench.Annotations;
using SharedGenomics.Workbench.Annotations.Requests;
using SharedGenomics.Workbench.Tests.TestObjects;

namespace SharedGenomics.Workbench.Tests.Annotations
{
    [TestClass]
    public class AnnotationRequestIntegrationTests
    {
        private IUnityContainer container;

        [TestInitialize]
        public void Setup()
        {
            this.container = new UnityContainer();
            this.container.RegisterType<IAnnotationRepositoryMediator, AnnotationRepositoryMediator>(new ContainerControlledLifetimeManager());
            this.container.RegisterType<IAnnotationRequestStatusNotifier, AnnotationRequestStatusNotifier>(new ContainerControlledLifetimeManager());
            this.container.RegisterType<IAnnotationRequestBuilderFactory, AnnotationRequestBuilderFactory>();
            this.container.RegisterType<IAnnotationPlanFactory, StaticAnnotationPlanFactory>();
            this.container.RegisterType<IAnalysisResultSetAnnotator, ResultSetAnnotator>();

            //setup annotation manager - only have google search annotations as active
            var annotationManager = new TestAnnotationManager { ActiveAnnotationDescriptors = AnnotationDescriptor.GetDescriptorForKey(AnnotationKey.GoogleSearch).AsSingleton() };
            container.RegisterInstance<IAnnotationManager>(annotationManager, new ContainerControlledLifetimeManager());

            //setup request pipeline
            AnnotationRequestPipeline pipeline = new AnnotationRequestPipeline();
            pipeline.AddRequestStage<AggregateAnnotationRequestStage>();

            var lookupStage = this.container.Resolve<LookupAnnotationRequestStage>();
            pipeline.AddRequestStage(lookupStage);

            var executeStage = this.container.Resolve<ExecuteAnnotationRequestStage>();
            pipeline.AddRequestStage(executeStage);

            var updateStage = this.container.Resolve<RepositoryUpdateAnnotationRequestStage>();
            pipeline.AddRequestStage(updateStage);

            var notificationStage = this.container.Resolve<NotificationAnnotationRequestStage>();
            pipeline.AddRequestStage(notificationStage);

            pipeline.Fix();
            this.container.RegisterInstance(pipeline);
        }

        [TestMethod]
        public void ShouldCreateGoogleAnnotations()
        {
            var notifiables = 100000.Of(i => new TestAnnotationRequestStatusNotifier("rs" + i)).ToList();

            //register notifiables with notifier
            IAnnotationRequestStatusNotifier notifier = this.container.Resolve<IAnnotationRequestStatusNotifier>();
            notifiables.ForEach(n => notifier.AddNotificationFor(n));

            var requestQueue = this.container.Resolve<SharedGenomics.Workbench.Annotations.AnnotationRequestQueue>();

            //create 10 threads each of which will queue a request for 10000 of the notifiables in the list
            ParameterizedThreadStart threadFunc = si =>
            {
                int startIndex = (int)si;
                Enumerable.Range(startIndex, 10000).ForEach(i => requestQueue.QueueRequest(new AnnotationRequestInfo { Key = AnnotationKey.GoogleSearch, RootValue = "rs" + i }));
            };

            //create list of threads, start them and wait for them to finish
            var threads = 10.Of(i => new Thread(threadFunc)).ToList();
            10.Times(i => threads[i].Start(i*10000));
            threads.ForEach(t => t.Join());

            while (!notifiables.All(n => n.WasNotified)) { Thread.Sleep(100); }

            var annotations = this.container.Resolve<IAnnotationRepositoryMediator>();
            100000.Of(i => "rs" + i).ForEach(snp =>
            {
                Assert.IsNotNull(annotations.GetAnnotationForTarget(AnnotationKey.GoogleSearch, snp));
            });
        }

        [TestMethod]
        public void ShouldAnnotateResultSet()
        {
            var annotator = this.container.Resolve<IAnalysisResultSetAnnotator>();

            var results = new AnalysisResultSet<FastEpistasisAnalysisResult>();
            this.GetAnalysisResults().ForEach(r => results.Add(r));

            var annotations = annotator.Annotate(results);

            //wait for all annotations to complete...
            while (!annotations.AnnotationSets.All(s => s[AnnotationKey.GoogleSearch].Status == AnnotationRequestStatus.Finished))
            {
                Thread.Sleep(100);
            };
        }

        private IEnumerable<FastEpistasisAnalysisResult> GetAnalysisResults()
        {
            for (int i = 1; i < 1000; i++)
            {
                yield return new FastEpistasisAnalysisResult { SnpName = "rs" + i, Snp2 = "rs" + (i + 1) };
            }
            
        }
    }
}
